]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st: Move the theme node drawing state to StWidget
authorJasper St. Pierre <jstpierre@mecheye.net>
Fri, 5 Apr 2013 00:29:59 +0000 (20:29 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Fri, 3 May 2013 14:50:30 +0000 (10:50 -0400)
This ensures that two widgets sharing the same theme node won't trample
on each other's prerendered materials if the actors are of different
sizes. This also tries to be very careful to share as much as possible
during a transition.

This has the side effect that if a widget changes state a bunch of times,
we won't cache every state. Since we expect that state changes are
infrequent and that most cases we'll be able to use the texture cache
to do most of the heavy lifting, this cost is much more insignificant
than rendering a number of different actors with the same theme node
and different sizes.

https://bugzilla.gnome.org/show_bug.cgi?id=697274

src/st/st-theme-node-drawing.c
src/st/st-theme-node-private.h
src/st/st-theme-node-transition.c
src/st/st-theme-node-transition.h
src/st/st-theme-node.c
src/st/st-theme-node.h
src/st/st-widget.c

index 70d96378173b340409bd1450be5f9e4621dc2c2c..3250536e6687116ae0a8566587c04527d45f0723 100644 (file)
@@ -1269,53 +1269,6 @@ st_theme_node_prerender_background (StThemeNode *node,
   return texture;
 }
 
-void
-_st_theme_node_paint_state_free (StThemeNodePaintState *state)
-{
-  int corner_id;
-
-  if (state->background_texture != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->background_texture);
-  if (state->background_material != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->background_material);
-  if (state->background_shadow_material != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->background_shadow_material);
-  if (state->border_slices_texture != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->border_slices_texture);
-  if (state->border_slices_material != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->border_slices_material);
-  if (state->prerendered_texture != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->prerendered_texture);
-  if (state->prerendered_material != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->prerendered_material);
-  if (state->box_shadow_material != COGL_INVALID_HANDLE)
-    cogl_handle_unref (state->box_shadow_material);
-
-  for (corner_id = 0; corner_id < 4; corner_id++)
-    if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
-      cogl_handle_unref (state->corner_material[corner_id]);
-
-  _st_theme_node_paint_state_init (state);
-}
-
-void
-_st_theme_node_paint_state_init (StThemeNodePaintState *state)
-{
-  int corner_id;
-
-  state->background_texture = COGL_INVALID_HANDLE;
-  state->background_material = COGL_INVALID_HANDLE;
-  state->background_shadow_material = COGL_INVALID_HANDLE;
-  state->box_shadow_material = COGL_INVALID_HANDLE;
-  state->border_slices_texture = COGL_INVALID_HANDLE;
-  state->border_slices_material = COGL_INVALID_HANDLE;
-  state->prerendered_texture = COGL_INVALID_HANDLE;
-  state->prerendered_material = COGL_INVALID_HANDLE;
-
-  for (corner_id = 0; corner_id < 4; corner_id++)
-    state->corner_material[corner_id] = COGL_INVALID_HANDLE;
-}
-
 static void st_theme_node_paint_borders (StThemeNode           *node,
                                          StThemeNodePaintState *state,
                                          const ClutterActorBox *box,
@@ -1345,7 +1298,7 @@ st_theme_node_render_resources (StThemeNode           *node,
    * geometry change versus things that can be cached regardless, such as
    * a background image.
    */
-  _st_theme_node_paint_state_free (state);
+  st_theme_node_paint_state_free (state);
 
   state->alloc_width = width;
   state->alloc_height = height;
@@ -1939,12 +1892,12 @@ st_theme_node_paint_outline (StThemeNode           *node,
 
 void
 st_theme_node_paint (StThemeNode           *node,
+                     StThemeNodePaintState *state,
                      const ClutterActorBox *box,
                      guint8                 paint_opacity)
 {
   float width, height;
   ClutterActorBox allocation;
-  StThemeNodePaintState *state = &node->state;
 
   /* Some things take an ActorBox, some things just width/height */
   width = box->x2 - box->x1;
@@ -2064,7 +2017,54 @@ st_theme_node_paint (StThemeNode           *node,
     }
 }
 
-static void
+void
+st_theme_node_paint_state_free (StThemeNodePaintState *state)
+{
+  int corner_id;
+
+  if (state->background_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->background_texture);
+  if (state->background_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->background_material);
+  if (state->background_shadow_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->background_shadow_material);
+  if (state->border_slices_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->border_slices_texture);
+  if (state->border_slices_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->border_slices_material);
+  if (state->prerendered_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->prerendered_texture);
+  if (state->prerendered_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->prerendered_material);
+  if (state->box_shadow_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (state->box_shadow_material);
+
+  for (corner_id = 0; corner_id < 4; corner_id++)
+    if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
+      cogl_handle_unref (state->corner_material[corner_id]);
+
+  st_theme_node_paint_state_init (state);
+}
+
+void
+st_theme_node_paint_state_init (StThemeNodePaintState *state)
+{
+  int corner_id;
+
+  state->background_texture = COGL_INVALID_HANDLE;
+  state->background_material = COGL_INVALID_HANDLE;
+  state->background_shadow_material = COGL_INVALID_HANDLE;
+  state->box_shadow_material = COGL_INVALID_HANDLE;
+  state->border_slices_texture = COGL_INVALID_HANDLE;
+  state->border_slices_material = COGL_INVALID_HANDLE;
+  state->prerendered_texture = COGL_INVALID_HANDLE;
+  state->prerendered_material = COGL_INVALID_HANDLE;
+
+  for (corner_id = 0; corner_id < 4; corner_id++)
+    state->corner_material[corner_id] = COGL_INVALID_HANDLE;
+}
+
+void
 st_theme_node_paint_state_copy (StThemeNodePaintState *state,
                                 StThemeNodePaintState *other)
 {
@@ -2073,10 +2073,7 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
   if (state == other)
     return;
 
-  /* Check omitted for speed: */
-  /* g_return_if_fail (st_theme_node_paint_equal (node, other)); */
-
-  _st_theme_node_paint_state_free (state);
+  st_theme_node_paint_state_free (state);
 
   state->alloc_width = other->alloc_width;
   state->alloc_height = other->alloc_height;
@@ -2102,33 +2099,9 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
       state->corner_material[corner_id] = cogl_handle_ref (other->corner_material[corner_id]);
 }
 
-/**
- * st_theme_node_copy_cached_paint_state:
- * @node: a #StThemeNode
- * @other: a different #StThemeNode
- *
- * Copy cached painting state from @other to @node. This function can be used to
- * optimize redrawing cached background images when the style on an element changess
- * in a way that doesn't affect background drawing. This function must only be called
- * if st_theme_node_paint_equal (node, other) returns %TRUE.
- */
 void
-st_theme_node_copy_cached_paint_state (StThemeNode *node,
-                                       StThemeNode *other)
-{
-  st_theme_node_paint_state_copy (&node->state,
-                                  &other->state);
-}
-
-static void
 st_theme_node_paint_state_invalidate (StThemeNodePaintState *state)
 {
   state->alloc_width = 0;
   state->alloc_height = 0;
 }
-
-void
-st_theme_node_invalidate_paint_state (StThemeNode *node)
-{
-  st_theme_node_paint_state_invalidate (&node->state);
-}
index e90835ee60e429039ff0c8d7a42814890c115d77..0da4edb6624241a33cb63304851148d83e98cfa9 100644 (file)
 
 G_BEGIN_DECLS
 
-typedef struct _StThemeNodePaintState StThemeNodePaintState;
-
-struct _StThemeNodePaintState {
-  float alloc_width;
-  float alloc_height;
-
-  CoglHandle background_texture;
-  CoglHandle background_material;
-  CoglHandle border_slices_texture;
-  CoglHandle border_slices_material;
-  CoglHandle background_shadow_material;
-  CoglHandle box_shadow_material;
-  CoglHandle prerendered_texture;
-  CoglHandle prerendered_material;
-  CoglHandle corner_material[4];
-};
-
 struct _StThemeNode {
   GObject parent;
 
@@ -116,8 +99,6 @@ struct _StThemeNode {
   guint background_image_shadow_computed : 1;
   guint text_shadow_computed : 1;
   guint link_type : 2;
-
-  StThemeNodePaintState state;
 };
 
 struct _StThemeNodeClass {
@@ -128,9 +109,6 @@ struct _StThemeNodeClass {
 void _st_theme_node_ensure_background (StThemeNode *node);
 void _st_theme_node_ensure_geometry (StThemeNode *node);
 
-void _st_theme_node_paint_state_init (StThemeNodePaintState *state);
-void _st_theme_node_paint_state_free (StThemeNodePaintState *state);
-
 G_END_DECLS
 
 #endif /* __ST_THEME_NODE_PRIVATE_H__ */
index 9e565c8b11927b4a2bf809853c9316409d0a9ce0..c4b6ea15d7eb69a4f7b7c0376169672af76c7336 100644 (file)
@@ -33,6 +33,9 @@ struct _StThemeNodeTransitionPrivate {
   StThemeNode *old_theme_node;
   StThemeNode *new_theme_node;
 
+  StThemeNodePaintState old_paint_state;
+  StThemeNodePaintState new_paint_state;
+
   CoglHandle old_texture;
   CoglHandle new_texture;
 
@@ -75,6 +78,7 @@ on_timeline_new_frame (ClutterTimeline       *timeline,
 StThemeNodeTransition *
 st_theme_node_transition_new (StThemeNode *from_node,
                               StThemeNode *to_node,
+                              StThemeNodePaintState *old_paint_state,
                               guint        duration)
 {
   StThemeNodeTransition *transition;
@@ -90,6 +94,9 @@ st_theme_node_transition_new (StThemeNode *from_node,
   transition->priv->old_theme_node = g_object_ref (from_node);
   transition->priv->new_theme_node = g_object_ref (to_node);
 
+  st_theme_node_paint_state_copy (&transition->priv->old_paint_state,
+                                  old_paint_state);
+
   transition->priv->timeline = clutter_timeline_new (duration);
 
   transition->priv->timeline_completed_id =
@@ -106,6 +113,12 @@ st_theme_node_transition_new (StThemeNode *from_node,
   return transition;
 }
 
+StThemeNodePaintState *
+st_theme_node_transition_get_new_paint_state (StThemeNodeTransition *transition)
+{
+  return &transition->priv->new_paint_state;
+}
+
 void
 st_theme_node_transition_update (StThemeNodeTransition *transition,
                                  StThemeNode           *new_node)
@@ -134,6 +147,12 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
    */
   if (st_theme_node_equal (new_node, old_node))
     {
+      {
+        StThemeNodePaintState tmp = priv->old_paint_state;
+        priv->old_paint_state = priv->new_paint_state;
+        priv->new_paint_state = tmp;
+      }
+
       if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
         {
           if (direction == CLUTTER_TIMELINE_FORWARD)
@@ -162,15 +181,10 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
 
           clutter_timeline_set_duration (priv->timeline, new_duration);
 
-          /* If the change doesn't affect painting, we don't need to redraw,
-           * but we still need to replace the node so that we properly share
-           * caching with the painting that happens after the transition finishes.
-           */
-          if (!st_theme_node_paint_equal (priv->new_theme_node, new_node))
-              priv->needs_setup = TRUE;
-
           g_object_unref (priv->new_theme_node);
           priv->new_theme_node = g_object_ref (new_node);
+
+          st_theme_node_paint_state_invalidate (&priv->new_paint_state);
         }
     }
 }
@@ -285,7 +299,7 @@ setup_framebuffers (StThemeNodeTransition *transition,
   cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
               priv->offscreen_box.y2, priv->offscreen_box.y1,
               0.0, 1.0);
-  st_theme_node_paint (priv->old_theme_node, allocation, 255);
+  st_theme_node_paint (priv->old_theme_node, &priv->old_paint_state, allocation, 255);
   cogl_pop_framebuffer ();
 
   cogl_push_framebuffer (priv->new_offscreen);
@@ -293,7 +307,7 @@ setup_framebuffers (StThemeNodeTransition *transition,
   cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
               priv->offscreen_box.y2, priv->offscreen_box.y1,
               0.0, 1.0);
-  st_theme_node_paint (priv->new_theme_node, allocation, 255);
+  st_theme_node_paint (priv->new_theme_node, &priv->new_paint_state, allocation, 255);
   cogl_pop_framebuffer ();
 
   return TRUE;
@@ -408,6 +422,9 @@ st_theme_node_transition_dispose (GObject *object)
   priv->timeline_completed_id = 0;
   priv->timeline_new_frame_id = 0;
 
+  st_theme_node_paint_state_free (&priv->old_paint_state);
+  st_theme_node_paint_state_free (&priv->new_paint_state);
+
   G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object);
 }
 
@@ -425,6 +442,9 @@ st_theme_node_transition_init (StThemeNodeTransition *transition)
   transition->priv->old_offscreen = NULL;
   transition->priv->new_offscreen = NULL;
 
+  st_theme_node_paint_state_init (&transition->priv->old_paint_state);
+  st_theme_node_paint_state_init (&transition->priv->new_paint_state);
+
   transition->priv->needs_setup = TRUE;
 }
 
index d6fd9358a4d3753cca8de5bc8c4b7b1e39cbbc18..e78389e457f678375a768526c2b15e58387b823a 100644 (file)
@@ -56,6 +56,7 @@ GType st_theme_node_transition_get_type (void) G_GNUC_CONST;
 
 StThemeNodeTransition *st_theme_node_transition_new (StThemeNode *from_node,
                                                      StThemeNode *to_node,
+                                                     StThemeNodePaintState *old_paint_state,
                                                      guint        duration);
 
 void  st_theme_node_transition_update   (StThemeNodeTransition *transition,
@@ -69,6 +70,8 @@ void  st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition,
                                               const ClutterActorBox *allocation,
                                               ClutterActorBox       *paint_box);
 
+StThemeNodePaintState * st_theme_node_transition_get_new_paint_state (StThemeNodeTransition *transition);
+
 G_END_DECLS
 
 #endif
index 91e8f092739695628c329cc6780714272ee01f1f..ff712dcee8588459174cc77393376aaca41c58fe 100644 (file)
@@ -49,7 +49,6 @@ static void
 st_theme_node_init (StThemeNode *node)
 {
   node->transition_duration = -1;
-  _st_theme_node_paint_state_init (&node->state);
 }
 
 static void
@@ -152,8 +151,6 @@ st_theme_node_finalize (GObject *object)
   if (node->background_image)
     g_free (node->background_image);
 
-  _st_theme_node_paint_state_free (&node->state);
-
   G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
 }
 
index 4a5f58c46e1cb1b6df6ad0eb19fca44e3ab6ce51..9593bde6f613b13fa7ec7812c044a62969cd9a19 100644 (file)
@@ -94,6 +94,23 @@ typedef enum {
   ST_GRADIENT_RADIAL
 } StGradientType;
 
+typedef struct _StThemeNodePaintState StThemeNodePaintState;
+
+struct _StThemeNodePaintState {
+  float alloc_width;
+  float alloc_height;
+
+  CoglHandle background_texture;
+  CoglHandle background_material;
+  CoglHandle border_slices_texture;
+  CoglHandle border_slices_material;
+  CoglHandle background_shadow_material;
+  CoglHandle box_shadow_material;
+  CoglHandle prerendered_texture;
+  CoglHandle prerendered_material;
+  CoglHandle corner_material[4];
+};
+
 GType st_theme_node_get_type (void) G_GNUC_CONST;
 
 StThemeNode *st_theme_node_new (StThemeContext *context,
@@ -250,12 +267,15 @@ gboolean st_theme_node_paint_equal    (StThemeNode *node,
                                        StThemeNode *other);
 
 void st_theme_node_paint (StThemeNode            *node,
+                          StThemeNodePaintState  *state,
                           const ClutterActorBox  *box,
                           guint8                  paint_opacity);
 
-void st_theme_node_copy_cached_paint_state (StThemeNode *node,
-                                            StThemeNode *other);
-void st_theme_node_invalidate_paint_state  (StThemeNode *node);
+void st_theme_node_paint_state_init (StThemeNodePaintState *state);
+void st_theme_node_paint_state_free (StThemeNodePaintState *state);
+void st_theme_node_paint_state_copy (StThemeNodePaintState *state,
+                                     StThemeNodePaintState *other);
+void st_theme_node_paint_state_invalidate  (StThemeNodePaintState *node);
 
 G_END_DECLS
 
index 7133e856137439f29ac7b6b2b3c546bee35f9c29..737b3e8757d91cffcf88632593da3355f082ec63 100644 (file)
@@ -79,6 +79,8 @@ struct _StWidgetPrivate
    * that we can remove the pseudo classes on them. */
   StWidget *prev_last_child;
   StWidget *prev_first_child;
+
+  StThemeNodePaintState paint_state;
 };
 
 /**
@@ -296,7 +298,7 @@ st_widget_texture_cache_changed (StTextureCache *cache,
   if (!changed)
     return;
 
-  st_theme_node_invalidate_paint_state (node);
+  st_theme_node_paint_state_invalidate (&actor->priv->paint_state);
 
   if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (actor)))
     clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
@@ -355,6 +357,8 @@ st_widget_finalize (GObject *gobject)
   g_free (priv->accessible_name);
   g_free (priv->inline_style);
 
+  st_theme_node_paint_state_free (&priv->paint_state);
+
   G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
 }
 
@@ -441,7 +445,10 @@ st_widget_paint_background (StWidget *widget)
                                     &allocation,
                                     opacity);
   else
-    st_theme_node_paint (theme_node, &allocation, opacity);
+    st_theme_node_paint (theme_node,
+                         &widget->priv->paint_state,
+                         &allocation,
+                         opacity);
 }
 
 static void
@@ -1517,12 +1524,17 @@ st_widget_init (StWidget *actor)
   g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
   g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
                     G_CALLBACK (st_widget_texture_cache_changed), actor);
+
+  st_theme_node_paint_state_init (&priv->paint_state);
 }
 
 static void
 on_transition_completed (StThemeNodeTransition *transition,
                          StWidget              *widget)
 {
+  st_theme_node_paint_state_copy (&widget->priv->paint_state,
+                                  st_theme_node_transition_get_new_paint_state (transition));
+
   st_widget_remove_transition (widget);
 }
 
@@ -1549,9 +1561,6 @@ st_widget_recompute_style (StWidget    *widget,
 
   paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node);
 
-  if (paint_equal)
-    st_theme_node_copy_cached_paint_state (new_theme_node, old_theme_node);
-
   g_object_get (gtk_settings_get_default (),
                 "gtk-enable-animations", &animations_enabled,
                 NULL);
@@ -1574,6 +1583,7 @@ st_widget_recompute_style (StWidget    *widget,
           widget->priv->transition_animation =
             st_theme_node_transition_new (old_theme_node,
                                           new_theme_node,
+                                          &widget->priv->paint_state,
                                           transition_duration);
 
           g_signal_connect (widget->priv->transition_animation, "completed",
@@ -1589,6 +1599,9 @@ st_widget_recompute_style (StWidget    *widget,
       st_widget_remove_transition (widget);
     }
 
+  if (!paint_equal)
+    st_theme_node_paint_state_invalidate (&widget->priv->paint_state);
+
   g_signal_emit (widget, signals[STYLE_CHANGED], 0);
   widget->priv->is_style_dirty = FALSE;
 }